﻿using apitest.Application.Services;
using Microsoft.AspNetCore.StaticFiles;
using System.Text;
using System.Web;
using System.Xml;

namespace AT.Data.Core.ExtendData
{
    public class DExtend
    {
        private string _ns = "xml";
        private string _version = "1.0";
        private string _root = "datax";
        private List<Col> _datas = new List<Col>();

        public DExtend()
        {

        }

        public DExtend(string xml)
        {
            this.LoadData(xml);
        }

        public DExtend(string ns, string version)
        {
            _ns = ns;
            _version = version;
        }

        public DExtend CreateItem(Col item)
        {
            var notChar = "{}[]:;\",.|\\~!@#$%^&*()+=-";
            if (string.IsNullOrEmpty(item.Name)) throw new ArgumentNullException("name");
            foreach (var c in notChar.ToCharArray())
            {
                if (item.Name.Contains(c)) throw new Exception($"名称不能包含字符:{c}");
            }
            if (_datas.Any(i => i.Name == item.Name))
            {
                var d = _datas.FirstOrDefault(i => i.Name == item.Name);
                if (d != null) d.Value = item.Value;
            }
            else
            {
                _datas.Add(item);
            }
            
            return this;
        }

        public DList CreateList(string name)
        {
            var list = new DList();
            var item = new Col(name, list);
            this.CreateItem(item);
            return list;
        }

        private void LoadData(string xml)
        {
            if (string.IsNullOrEmpty(xml)) return;

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            if (xmlDoc == null) return;

            //root
            var root = xmlDoc.SelectSingleNode(_root);
            if (root == null) return;
            for (var i = 0; i < root.Attributes?.Count; i++)
            {
                if (root.Attributes[i].Name == "ns") this._ns = root.Attributes[i].Value;
                if (root.Attributes[i].Name == "v") this._version = root.Attributes[i].Value;
            }
            if (!xmlDoc.HasChildNodes) return;
            //item
            foreach (XmlNode node in xmlDoc.ChildNodes[0].ChildNodes)
            {
                var name = "";
                var type = ValueItemEnum.text.ToString();
                for (var i = 0; i < node.Attributes?.Count; i++)
                {
                    if (node.Attributes[i].Name == "n") name = node.Attributes[i].Value;
                    if (node.Attributes[i].Name == "t") type = node.Attributes[i].Value;
                }
                if (type.ToLower() == ValueItemEnum.text.ToString().ToLower())
                {
                    var val = Web.HtmlDecode(node.InnerText);
                    this.CreateItem(new Col(name, val));
                }
                else if (type.ToLower() == ValueItemEnum.list.ToString().ToLower())
                {
                    var list = this.CreateList(name);
                    foreach (XmlNode n in node.ChildNodes)
                    {
                        var row = list.CreateRow();
                        foreach (XmlNode nn in n.ChildNodes)
                        {
                            name = nn.Attributes["n"].Value;
                            var val = Web.HtmlDecode(nn.InnerText);
                            row.AddCol(new Col(name, val));
                        }
                    }
                }
            }
        }

        public List<Col> ToData() => this._datas;

        public string ToJson()
        {
            //return Newtonsoft.Json.JsonConvert.SerializeObject(_datas);

            var xbuilder = new StringBuilder();
            xbuilder.Append("{");
            var t1 = 0;
            foreach (var item in _datas)
            {
                var itemType = ValueItemEnum.text.ToString();
                if (item.Value is DText) itemType = ValueItemEnum.text.ToString();
                if (item.Value is DList) itemType = ValueItemEnum.list.ToString();

                
                if (itemType == ValueItemEnum.text.ToString())
                {
                    DText v = (DText)item.Value;
                    if (v.Value.Contains("\r\n"))
                        v.Value = v.Value.Replace("\r\n", "\\r\\n");
                    xbuilder.Append($"\"{item.Name}\":\"{Web.HtmlEncode(v.Value)}\"");
                }
                else if (itemType == ValueItemEnum.list.ToString())
                {
                    xbuilder.Append($"{item.Name}:[");
                    var list = (DList)item.Value;
                    var t2 = 0;
                    foreach (var v in list.Rows)
                    {
                        xbuilder.Append("{");
                        var t3 = 0;
                        foreach (var vv in v.Cols)
                        {
                            DText t = (DText)vv.Value;
                            xbuilder.Append($"\"{item.Name}\":\"{Web.HtmlEncode(t.Value)}\"");
                            t3++;
                            if (t3 < v.Cols.Count) xbuilder.Append(",");
                        }
                        xbuilder.Append("}");
                        t2++;
                        if (t2 < list.Rows.Count) xbuilder.Append(",");
                        
                    }
                    xbuilder.Append("]");
                }
                t1++;
                if (t1 < _datas.Count) xbuilder.Append(",");
                
            }
            xbuilder.Append("}");
            return xbuilder.ToString();

        }

        public string ToXML()
        {
            var xbuilder = new StringBuilder();
            xbuilder.Append($"<{_root} ns=\"{this._ns}\" v=\"{this._version}\">");

            xbuilder.Append(this.ToItemXML());

            xbuilder.Append($"</{_root}>");
            return xbuilder.ToString();
        }

        public string ToItemXML()
        {
            var xbuilder = new StringBuilder();
            foreach (var item in _datas)
            {
                var itemType = ValueItemEnum.text.ToString();
                if (item.Value is DText) itemType = ValueItemEnum.text.ToString();
                if (item.Value is DList) itemType = ValueItemEnum.list.ToString();

                xbuilder.Append($"<i t=\"{itemType}\" n=\"{item.Name}\">");
                if (itemType == ValueItemEnum.text.ToString())
                {
                    DText v = (DText)item.Value;
                    xbuilder.Append(Web.HtmlEncode(v.Value));
                }
                else if (itemType == ValueItemEnum.list.ToString())
                {
                    var list = (DList)item.Value;
                    foreach (var v in list.Rows)
                    {
                        xbuilder.Append("<r>");
                        foreach (var vv in v.Cols)
                        {
                            xbuilder.Append($"<i t=\"{ValueItemEnum.text.ToString()}\" n=\"{vv.Name}\">");
                            DText t = (DText)vv.Value;
                            xbuilder.Append(Web.HtmlEncode(t.Value));
                            xbuilder.Append($"</i>");
                        }
                        xbuilder.Append("</r>");
                    }
                }
                xbuilder.Append($"</i>");
            }
            return xbuilder.ToString();
        }
    }
    public static class Web
    {
        public static string HtmlEncode(string html)
        {
            if (!string.IsNullOrEmpty(html))
                return HttpUtility.HtmlEncode(html);
            return string.Empty;
        }

        public static string HtmlDecode(string html)
        {
            if (!string.IsNullOrEmpty(html))
                return HttpUtility.HtmlDecode(html);
            return string.Empty;
        }

        public static string GetContentType(string ext)
        {
            var provider = new FileExtensionContentTypeProvider();
            try
            {
                return provider.Mappings[ext];
            }
            catch
            {
                return provider.Mappings[".txt"];
            }
        }
    }
}
